package com.a1anwang.okble.demo.ability;

import com.a1anwang.okble.client.scan.BLEScanResult;
import com.a1anwang.okble.client.scan.DeviceScanCallBack;
import com.a1anwang.okble.client.scan.OKBLEScanManager;
import com.a1anwang.okble.common.OKBLEDataUtils;
import com.a1anwang.okble.demo.APP;
import com.a1anwang.okble.demo.ResourceTable;
import com.a1anwang.okble.demo.common.MyLinkedHashMap;

import com.a1anwang.okble.demo.utils.ToastUtil;
import com.a1anwang.okble.demo.widget.HeadDefaultComponent;
import com.a1anwang.okble.demo.widget.IRefresh;
import com.a1anwang.okble.demo.widget.RefreshComponent;
import ohos.aafwk.ability.Ability;
import ohos.aafwk.content.Intent;
import ohos.aafwk.content.Operation;
import ohos.agp.components.*;
import ohos.agp.utils.Color;
import ohos.app.Context;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;

import java.util.*;

public class ScanAbility extends Ability implements ListContainer.ItemClickedListener, Component.ClickedListener {
    private static final HiLogLabel LABEL = new HiLogLabel(HiLog.LOG_APP, 0x00221, "OKBLE");
    private OKBLEScanManager scanManager;
    private ListContainer listview;
    private ItemProvider provider;
    private MyLinkedHashMap<String, BLEScanResult> scanedResults;
    private HashMap<String, Boolean> openflags;
    private Image btnLeft;
    private RefreshComponent myView;

    @Override
    protected void onStart(Intent intent) {
        super.onStart(intent);
        setUIContent(ResourceTable.Layout_ability_scan);
        initView();
    }

    public void initView() {
        scanedResults = new MyLinkedHashMap<>();
        openflags = new HashMap<>();
        scanManager = new OKBLEScanManager(this);
        scanManager.setScanCallBack(scanCallBack);
        btnLeft = (Image) findComponentById(ResourceTable.Id_btn_left);
        btnLeft.setClickedListener(this);
        listview = (ListContainer) findComponentById(ResourceTable.Id_listview_scan);
        provider = new ItemProvider(this);

        getWindow().setStatusBarColor(Color.getIntColor("#1E90FF"));
        logInfo("initView");

        // 获取到刷新组件
        myView = (RefreshComponent) findComponentById(ResourceTable.Id_myView);
        // 设置头部刷新样式,可自定义样式
        HeadDefaultComponent headDefaultComponent = new HeadDefaultComponent(this);
        // 添加样式到头部
        myView.setHeadComponent(headDefaultComponent);
        // 设置刷新回调
        myView.setRefreshListener(new IRefresh.RefreshListener() {
            @Override
            public void onRefresh() {

                new EventHandler(EventRunner.getMainEventRunner()).postTask(new Runnable() {
                    @Override
                    public void run() {
                        scanedResults.clear();
                        openflags.clear();
                        provider.notifyDataChanged();
                        scanManager.startScan();
                        // 数据更新完  结束掉刷新
                        myView.refreshFinish();
                    }
                }, 500);
            }

            @Override
            public boolean enableRefresh() {
                return false;
            }
        });
        listview.setItemProvider(provider);
        listview.setItemClickedListener(this);
    }

    @Override
    protected void onActive() {
        super.onActive();
        scanManager.startScan();
    }

    @Override
    protected void onInactive() {
        super.onInactive();
        scanManager.stopScan();
    }

    @Override
    public void onClick(Component component) {
        switch (component.getId()) {
            case ResourceTable.Id_btn_left:
                onBackPressed();
                break;
            default:
                break;
        }
    }

    @Override
    public void onItemClicked(ListContainer listContainer, Component component, int i, long l) {
        scanManager.stopScan();
        BLEScanResult scanResult = scanedResults.get(i);
        APP.getApp().setScanResult(scanResult);
        Intent intent = new Intent();
        Operation operation = new Intent.OperationBuilder()
                .withBundleName(getBundleName())
                .withAbilityName(DeviceDetailAbility.class.getName())
                .build();
        intent.setOperation(operation);
        startAbility(intent);
    }

    DeviceScanCallBack scanCallBack = new DeviceScanCallBack() {
        @Override
        public void onBLEDeviceScan(BLEScanResult device, int rssi) {
            int value[] = scanedResults.put(device.getMacAddress(), device);
            new EventHandler(EventRunner.getMainEventRunner()).postTask(new Runnable() {
                @Override
                public void run() {
                    if (value[1] == 1) {
                        // 这是新增数据
                        provider.notifyDataChanged();
                    } else {
                        // 这是重复数据,刷新rssi
                        int index = value[0];
                        updatePosition(index);
                    }

                }

            });
        }

        @Override
        public void onFailed(int code) {
            switch (code) {
                case DeviceScanCallBack.SCAN_FAILED_BLE_NOT_SUPPORT:
                    showToast("该设备不支持BLE");
                    break;
                case DeviceScanCallBack.SCAN_FAILED_BLUETOOTH_DISABLE:
                    showToast("请打开手机蓝牙");
                    break;
                case DeviceScanCallBack.SCAN_FAILED_LOCATION_PERMISSION_DISABLE:
                    showToast("请授予位置权限以扫描周围的蓝牙设备");
                    break;
                case DeviceScanCallBack.SCAN_FAILED_LOCATION_PERMISSION_DISABLE_FOREVER:
                    showToast("位置权限被您永久拒绝,请在设置里授予位置权限以扫描周围的蓝牙设备");
                    break;
            }
        }
        @Override
        public void onStartSuccess() {

        }
    };

    private void showToast(String msg) {
        ToastUtil.toast(this,msg);
    }

    private void updatePosition(int posi) {
        int visibleFirstPosi = listview.getFirstVisibleItemPosition();
        int visibleLastPosi = listview.getLastVisibleItemPosition();
        if (posi >= visibleFirstPosi && posi <= visibleLastPosi) {
            Component view = listview.getComponentAt(posi - visibleFirstPosi);
            if (view != null) {
                ViewHolder holder = (ViewHolder) view.getTag();
                if (holder != null) holder.tvRssi.setText(scanedResults.get(posi).getRssi() + "");
            }
        }
    }

    /**
     * ItemProvider 适配器
     *
     * @since 2021-05-14
     */
    private class ItemProvider extends BaseItemProvider {

        private Context context;
        ItemProvider(Context context) {
            logInfo("ItemProvider");
            this.context = context;
        }

        @Override
        public int getCount() {
            logInfo("getCount:" + (scanedResults == null ? 0 : scanedResults.size()));
            return scanedResults == null ? 0 : scanedResults.size();
        }

        @Override
        public Object getItem(int position) {
            if (scanedResults != null && position >= 0 && position < scanedResults.size()) {
                return scanedResults.get(position);
            }
            return null;
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public Component getComponent(int position, Component convertView, ComponentContainer componentContainer) {
            logInfo("getComponent");
            ViewHolder viewholder;
            if (convertView == null) {
                viewholder = new ViewHolder();
                convertView = LayoutScatter.getInstance(context).parse(ResourceTable.Layout_item_scanresult, null, false);
                convertView.setTag(viewholder);
                viewholder.tvName = (Text) convertView.findComponentById(ResourceTable.Id_tv_name);
                viewholder.tvMac = (Text) convertView.findComponentById(ResourceTable.Id_tv_mac);
                viewholder.tvRssi = (Text) convertView.findComponentById(ResourceTable.Id_tv_rssi);
                viewholder.layoutExpand = (DirectionalLayout) convertView.findComponentById(ResourceTable.Id_layout_expand);
                viewholder.tvRaw = (Text) convertView.findComponentById(ResourceTable.Id_tv_raw);
                viewholder.tvServiceUuid = (Text) convertView.findComponentById(ResourceTable.Id_tv_service_uuid);
                viewholder.tvCompanyId = (Text) convertView.findComponentById(ResourceTable.Id_tv_company_id);
                viewholder.tvCompanyValue = (Text) convertView.findComponentById(ResourceTable.Id_tv_company_value);
                viewholder.tvServiceData = (Text) convertView.findComponentById(ResourceTable.Id_tv_service_data);
                viewholder.tvLocalname = (Text) convertView.findComponentById(ResourceTable.Id_tv_localname);
                viewholder.btnExpand = (Image) convertView.findComponentById(ResourceTable.Id_btn_expand);
                viewholder.btnExpand.setClickedListener(component -> {
                    int i = (Integer) component.getTag();
                    BLEScanResult bleScanResultInner = scanedResults.get(i);
                    if (openflags.containsKey(bleScanResultInner.getMacAddress())) {
                        Boolean flag = openflags.get(bleScanResultInner.getMacAddress());
                        openflags.put(bleScanResultInner.getMacAddress(), Boolean.valueOf(!flag.booleanValue()));
                    } else {
                        openflags.put(bleScanResultInner.getMacAddress(), Boolean.valueOf(true));
                    }
                    provider.notifyDataChanged();
                });
            } else {
                viewholder = (ViewHolder) convertView.getTag();
            }
            viewholder.btnExpand.setTag(Integer.valueOf(position));
            BLEScanResult bleScanResultInner = scanedResults.get(position);
            boolean opened;
            if (openflags.containsKey(bleScanResultInner.getMacAddress())) {
                opened = openflags.get(bleScanResultInner.getMacAddress()).booleanValue();
            } else {
                opened = false;
            }
            if (opened) {
                viewholder.btnExpand.setRotation(180);
                viewholder.layoutExpand.setVisibility(Component.VISIBLE);
                viewholder.tvRaw.setText("0x" + OKBLEDataUtils.BytesToHexString(bleScanResultInner.getAdvertisingData()));
                ((Component) viewholder.tvRaw.getComponentParent()).setVisibility(Component.VISIBLE);
                List<String> serviceuuids = bleScanResultInner.getServiceUuids();
                if (serviceuuids != null && serviceuuids.size() > 0) {
                    viewholder.tvServiceUuid.setText(formatServiceUuids(serviceuuids));
                    ((Component) viewholder.tvServiceUuid.getComponentParent()).setVisibility(Component.VISIBLE);
                } else {
                    ((Component) viewholder.tvServiceUuid.getComponentParent()).setVisibility(Component.HIDE);
                }
                Map<Integer, byte[]> array = bleScanResultInner.getManufacturerSpecificData();
                if (array != null && array.size() > 0) {
                    int key = (int) array.keySet().toArray()[0];
                    viewholder.tvCompanyId.setText("0x" + OKBLEDataUtils.intToHexStr(key));
                    HiLog.error(LABEL, "Company id:" + OKBLEDataUtils.intToHexStr(key));
                    viewholder.tvCompanyValue.setText("0x" + OKBLEDataUtils.BytesToHexString(array.get(key)));
                    HiLog.error(LABEL, "data:" + OKBLEDataUtils.BytesToHexString(array.get(key)));
                    ((Component) viewholder.tvCompanyId.getComponentParent().getComponentParent()).setVisibility(Component.VISIBLE);
                } else {
                    ((Component) viewholder.tvCompanyId.getComponentParent().getComponentParent()).setVisibility(Component.HIDE);
                }
                Map<String, byte[]> servicesDatas = bleScanResultInner.getServiceData();
                if (servicesDatas != null && servicesDatas.size() > 0) {
                    viewholder.tvServiceData.setText(formatServiceData(servicesDatas));
                    ((Component) viewholder.tvServiceData.getComponentParent()).setVisibility(Component.VISIBLE);
                } else {
                    ((Component) viewholder.tvServiceData.getComponentParent()).setVisibility(Component.HIDE);
                }
                String completeLocalName = bleScanResultInner.getCompleteLocalName();
                if (completeLocalName != null) {
                    viewholder.tvLocalname.setText(completeLocalName);
                    ((Component) viewholder.tvLocalname.getComponentParent()).setVisibility(Component.VISIBLE);
                } else {
                    ((Component) viewholder.tvLocalname.getComponentParent()).setVisibility(Component.HIDE);
                }
            } else {
                viewholder.btnExpand.setRotation(0);
                viewholder.layoutExpand.setVisibility(Component.HIDE);
            }
            String name = bleScanResultInner.getCompleteLocalName();
            viewholder.tvName.setText(name == null ? "[null]" : name);
            viewholder.tvMac.setText(bleScanResultInner.getMacAddress());
            viewholder.tvRssi.setText(bleScanResultInner.getRssi() + "");
            return convertView;
        }
    }
    /**
     * ViewHolder ViewHolder
     *
     * @since 2021-05-14
     */
    static class ViewHolder {
        Text tvName;
        Text tvMac;
        Text tvRssi;
        Image btnExpand;
        Text tvRaw;
        Text tvServiceUuid;
        Text tvCompanyId;
        Text tvCompanyValue;
        Text tvServiceData;
        Text tvLocalname;
        DirectionalLayout layoutExpand;
    }

    private String formatServiceUuids(List<String> serviceuuids) {
        StringBuilder builder = new StringBuilder();
        for (String uuid : serviceuuids) {
            builder.append("0x");
            builder.append(uuid);
            builder.append(",");
        }
        String str = builder.toString();
        return str.substring(0, str.length() - 1);
    }

    private String formatServiceData(Map<String, byte[]> servicesDatas) {
        StringBuilder builder = new StringBuilder();
        for (Map.Entry<String, byte[]> entry : servicesDatas.entrySet()) {
            builder.append("uuid:0x");
            builder.append(entry.getKey());
            builder.append(" data:0x");
            builder.append(OKBLEDataUtils.BytesToHexString(servicesDatas.get(entry.getKey())));
            builder.append("\n");
        }
        String str = builder.toString();
        return str.substring(0, str.length() - 1);
    }

    private void logInfo(String msg) {
        HiLog.info(LABEL, msg);
    }
}
